import {ref} from 'vue';
import {pack, unpack, UnpackedMessage, PeerInfo, FileInfo, MessageCallback} from './packer.ts';
import {generateUUID} from './uuid.ts';
import {useAuthStore} from "@/store/modules/auth.ts";

/**
 * WebSocket 单例封装
 * 支持自动重连、ping/pong 保活、文件/文本发送
 */
export class Websocket {
    private static instance: Websocket | null = null;
    public url: string;
    public userid: string;
    private ws: WebSocket | null = null;
    private isManuallyClosed = false;
    private reconnectTimer: ReturnType<typeof setTimeout> | null = null;
    public isConnected = ref(false);
    private hasConnectedOnce = false;
    private reconnectCount = 0;
    public reconnectAttempts = ref(0);
    private maxReconnect = 5;
    private baseReconnectDelay = 1000;
    private maxFileSize = 1 * 1024 * 1024;
    private messageCallback: MessageCallback | null = null;

    private get authStore() {
        return useAuthStore();
    }

    constructor(url: string, userid: string) {
        this.url = url;
        this.userid = userid;
    }

    /** 获取单例 */
    public static getInstance(url?: string, userid?: string): Websocket {
        if (!Websocket.instance) {
            if (!url || !userid) {
                throw new Error('WebSocket 初始化失败：首次调用 getInstance 时必须提供连接地址和用户唯一标识');
            }
            Websocket.instance = new Websocket(url, userid);
        } else {
            if (url) Websocket.instance.url = url;
            if (userid) Websocket.instance.userid = userid;
        }
        return Websocket.instance;
    }

    /** 建立连接 */
    public connect(): Promise<void> {
        this.isManuallyClosed = false;
        return new Promise((resolve, reject) => this._connect(resolve, reject));
    }

    /** 主动关闭连接 */
    public close(code?: number, reason?: string): void {
        this.isManuallyClosed = true;
        this.isConnected.value = false;
        if (this.reconnectTimer) {
            clearTimeout(this.reconnectTimer);
            this.reconnectTimer = null;
        }
        this.ws?.close(code ?? 1000, reason);
        this.ws = null;
    }

    /** 设置消息回调 */
    public setMessageCallback(callback: MessageCallback): void {
        this.messageCallback = callback;
    }

    /** 移除消息回调 */
    public removeMessageCallback(): void {
        this.messageCallback = null;
    }

    /** 销毁实例 */
    public destroy(): void {
        this.close();
        this.removeMessageCallback();
        Websocket.instance = null;
    }

    /** 内部连接逻辑 */
    private _connect(resolve?: () => void, reject?: (error: Event | Error) => void): void {
        this.ws?.close();
        if (this.reconnectTimer) {
            clearTimeout(this.reconnectTimer);
            this.reconnectTimer = null;
        }

        if (!this.url || !this.userid) {
            reject?.(new Error('连接地址和用户唯一标识不能为空'));
            return;
        }

        try {
            const url = `${this.url}?userid=${encodeURIComponent(this.userid)}`;
            this.ws = new WebSocket(url);
            this.ws.binaryType = 'arraybuffer';

            this.ws.onopen = () => {
                console.log('✅ WebSocket 已连接');
                this.isConnected.value = true;
                this.hasConnectedOnce = true;
                this.reconnectCount = 0;
                this.reconnectAttempts.value = 0;
                resolve?.();
            };

            this.ws.onclose = (event) => {
                console.warn('❌ WebSocket 已关闭', event.code, event.reason);
                this.isConnected.value = false;
                if (!this.isManuallyClosed && this.hasConnectedOnce && event.code !== 1000) {
                    this._reconnect();
                }
            };

            this.ws.onerror = (event) => {
                console.error('WebSocket 错误', event);
                this.isConnected.value = false;
                if (!this.hasConnectedOnce) {
                    this.ws?.close(); // ✅ 修复残留 ws
                    reject?.(new Error('❌ WebSocket 连接错误'));
                }
            };

            this.ws.onmessage = (event) => {
                if (!(event.data instanceof ArrayBuffer)) {
                    console.warn('收到非二进制消息，忽略:', event.data);
                    return;
                }
                try {
                    const msg = unpack(event.data);
                    if (msg.type === 101) {
                        this._handlePing(msg);
                    } else {
                        const safeFiles: FileInfo[] = Array.isArray(msg.files) ? msg.files : [];
                        this._handleMessage(msg, safeFiles);
                    }
                } catch (err) {
                    console.error('消息解析失败:', err);
                }
            };
        } catch (err) {
            reject?.(err as Error);
        }
    }

    /** 处理 ping 消息 */
    private _handlePing(msg: UnpackedMessage): void {
        if (this.ws?.readyState === WebSocket.OPEN) {
            const pongPeer: Partial<PeerInfo> = {
                sender: this.authStore.userInfo.id,
                receiver: 'server',
                notice_type: 1,
                files: []
            };
            const pongBuffer = pack(102, msg.uuid, Math.floor(Date.now() / 1000), 1, 1, pongPeer, 'pong');
            this.ws.send(pongBuffer);
        }
    }

    /** 自动重连逻辑 */
    private _reconnect(): void {
        if (this.reconnectCount >= this.maxReconnect) {
            console.warn('⚠️ 达到最大重连次数，停止重连');
            return;
        }

        if (this.reconnectTimer) {
            clearTimeout(this.reconnectTimer);
            this.reconnectTimer = null; // ✅ 防止多定时器
        }

        const delay = Math.min(this.baseReconnectDelay * 2 ** this.reconnectCount, 30000);
        console.log(`🔄 ${this.reconnectCount + 1}/${this.maxReconnect} 次重连中...`);

        this.reconnectTimer = setTimeout(() => {
            this.reconnectCount++;
            this.reconnectAttempts.value = this.reconnectCount;
            this._connect();
        }, delay);
    }

    /** 统一处理普通消息 */
    private _handleMessage(msg: UnpackedMessage, safeFiles: FileInfo[]): void {
        try {
            const textTypes = [1, 5, 7, 8, 9, 101, 102];
            if (textTypes.includes(msg.type)) {
                if (msg.payload instanceof Uint8Array) {
                    msg.payload = new TextDecoder().decode(msg.payload);
                }
            } else {
                if (!(msg.payload instanceof Blob)) {
                    msg.payload = new Blob([msg.payload as Uint8Array], {type: safeFiles[0]?.mime ?? undefined});
                }
            }
        } catch (err) {
            console.error('解析消息 payload 失败:', err);
        }

        this.messageCallback?.(msg);
    }

    /** 发送消息（文本、JSON、文件） */
    public async sendMessage(
        receiver: string | string[],
        content: string | object | File,
        noticeType = 1,
        group: string | null = null,
    ): Promise<string> {
        if (!this.ws || this.ws.readyState !== WebSocket.OPEN) {
            throw new Error('WebSocket未连接');
        }

        const uuid = generateUUID();
        const peer: Partial<PeerInfo> = {
            sender: this.authStore.userInfo.id,
            receiver,
            group,
            notice_type: noticeType,
            files: []
        };

        if (content instanceof File) {
            if (content.size > this.maxFileSize) {
                throw new Error(`文件过大，最大支持 ${this.maxFileSize / (1024 * 1024)} MB`);
            }
            await this._sendFile(uuid, content, peer);
        } else {
            await this._sendTextOrJson(uuid, content, peer);
        }
        return uuid;
    }

    /** 发送文件 */
    private async _sendFile(uuid: string, file: File, peer: Partial<PeerInfo>): Promise<void> {
        const mainType = file.type.split('/')[0] || 'other';
        const typeMap: Record<string, number> = {image: 2, video: 4, audio: 6};
        const type = typeMap[mainType] ?? 3; // ✅ 修复安全类型
        const files: FileInfo[] = [{name: file.name, size: file.size, mime: file.type}];
        const arrayBuffer = await file.arrayBuffer();
        const buffer = pack(type, uuid, Math.floor(Date.now() / 1000), 1, 1, {...peer, files}, arrayBuffer);
        await this._sendBuffer(buffer);
    }

    /** 发送文本或 JSON */
    private async _sendTextOrJson(uuid: string, content: string | object, peer: Partial<PeerInfo>): Promise<void> {
        const payloadStr = typeof content === 'string' ? content : JSON.stringify(content);
        const buffer = pack(1, uuid, Math.floor(Date.now() / 1000), 1, 1, peer, payloadStr);
        await this._sendBuffer(buffer);
    }

    /** 通用 buffer 发送逻辑 */
    private _sendBuffer(buffer: ArrayBuffer): Promise<void> {
        return new Promise((resolve, reject) => {
            const start = Date.now();
            const timeout = 10000;

            const trySend = () => {
                if (this.ws?.readyState === WebSocket.OPEN) {
                    try {
                        this.ws.send(buffer);
                        resolve();
                    } catch (err) {
                        reject(err);
                    }
                } else if (Date.now() - start > timeout) {
                    reject(new Error('发送超时'));
                } else {
                    setTimeout(trySend, 50);
                }
            };

            trySend();
        });
    }
}
